package org.xnat.xnatfs.webdav;

import java.io.IOException;
import java.util.Date;

import org.apache.log4j.Logger;
import org.xnat.xnatfs.asset.IAsset;
import org.xnat.xnatfs.exception.RequestException;

import com.bradmcevoy.http.Auth;
import com.bradmcevoy.http.PropFindableResource;
import com.bradmcevoy.http.Request;
import com.bradmcevoy.http.Resource;
import com.bradmcevoy.http.Request.Method;

public abstract class VirtualResource implements Resource, PropFindableResource {
  private static final Logger logger = Logger.getLogger ( VirtualResource.class );

  // until we are able to pull out the last-modified date from the REST API,
  // generate a new date upon every request.
  // private static final Date sDate = new Date ();
  private Date date = new Date ();

  // Two phase asset retrieval
  // TODO consider refactoring (weakly typed)
  private IAsset asset;
  private IAsset assetHead;

  String path;
  String name;
  XNATFS xnatfs;
  String absolutePath;

  public VirtualResource ( XNATFS x, String p, String n ) {
    xnatfs = x;
    path = p;
    name = n;
    if ( path == null && name.equals ( "/" ) ) {
      absolutePath = "/";
    } else {
      if ( path.equals ( "/" ) ) {
        absolutePath = path + name;
      } else {
        absolutePath = path + "/" + name;
      }
    }
  }

  @Override
  public Object authenticate ( String user, String password ) {
    // We will always authenticate the user by returning their username, and
    // relying on authorise() to actually contact the resource and check the
    // user's permission to access that resource. TODO review & test this
    // decision
    if ( user == null || password == null ) {
      return null;
    }
    return user;
  }

  @Override
  public boolean authorise ( Request request, Method method, Auth auth ) {
    if ( auth == null || auth.getTag () == null ) {
      return false;
    }
    logger.debug ( "Checking authorization for " + auth.getUser () );
    boolean authorised = false;
    try {
      authorised = retreiveAssetHead ().authorised ();
    } catch ( RequestException e ) {
      logger.warn ( "Got error when authorizing", e );
    }
    return authorised;
  }

  /**
   * @return whether the resource actually exists via the REST API
   */
  public boolean exists () {
    return true;
    /*
    boolean exists = false;
    try {
      exists = retreiveAssetHead ().found ();
    } catch ( RequestException e ) {
      logger.warn ( "Got error when checking existence", e );
    }
    return exists;
    */
  }

  protected IAsset retreiveAsset () throws RequestException {
    if ( asset == null ) {
      asset = requestAsset ( getRestUrl () );
    }
    return asset;
  }

  protected IAsset retreiveAssetHead () throws RequestException {
    if ( assetHead == null ) {
      if ( asset != null ) {
        // since mAsset contains the head plus a body, we can use the mAsset as
        // the mAssetHead
        assetHead = asset;
      }
      assetHead = xnatfs.getAssetProvider ().getAssetHead ( getRestUrl (), getAuth () );
    }
    return assetHead;
  }

  /**
   * Close the asset to release the connection back to the connection pool.
   */
  public void close () {
    if ( asset != null ) {
      try {
        asset.close ();
      } catch ( IOException e ) {
        logger.warn ( "Could not close asset, continuing...", e );
      }
    }
  }

  protected abstract IAsset requestAsset ( String url ) throws RequestException;

  /**
   * 
   * @return the definitive URL for an HTTP client to get the REST version of this Resource
   */
  protected abstract String getRestUrl ();

  public String getAbsoluteName () {
    if ( absolutePath != null ) {
      return absolutePath + "/" + name;
    } else {
      return name;
    }
  }

  public String getAbsolutePath () {
    return absolutePath;
  }

  public Auth getAuth () {
    return xnatfs.getAuth ();
  }

  @Override
  public String checkRedirect ( Request request ) {
    // FIXME implement
    return null;
  }

  @Override
  public Date getCreateDate () {
    return date;
  }

  @Override
  public Date getModifiedDate () {
    return date;
  }

  @Override
  public String getName () {
    return name;
  }

  @Override
  public String getRealm () {
    return "XNAT WebDAV";
  }

  @Override
  public String getUniqueId () {
    return absolutePath;
  }

  // @Override
  // public LockToken getCurrentLock () {
  // return null;
  // }
  //
  // @Override
  // public LockResult lock ( LockTimeout timeout, LockInfo lockInfo ) {
  // return xnatfs.getLockManager ().lock ( timeout, lockInfo, this );
  // }
  //
  // @Override
  // public LockResult refreshLock ( String token ) {
  // return xnatfs.getLockManager ().refresh ( token, this );
  // }
  //
  // @Override
  // public void unlock ( String tokenId ) {
  // xnatfs.getLockManager ().unlock ( tokenId, this );
  // }
}
